package de.hska.swlab.iwwdnw.model;

import java.util.Observable;
import de.hska.swlab.iwwdnw.helper.RandInt;

/**
 * Kapselt die Logik des Spiels
 */
public class IWWWDNW extends Observable {

	private final int ANZAHL_KATEGORIEN = 4;
	private final int MAX_WUERFE = 3;

	// die aus der Textdatei eingelesen Kategorien
	private Kategorie[] kategorien;
	private Status status;
	private int anzahlSpieler = 0;
	private Spielfeld spielfeld;
	private String[] spielerNamen;
	// die 4 für das Spiel ausgewaehlten Kategorien
	private int[] spielKategorien;

	private int momentanerWurf;
	private int momentanerSpieler;
	private Frage momentaneFrage;
	private int momentaneKategorieId;
	private int momentanerWissensstreiter;
	private WissensstreiterFeld momentanKollidiertesFeld;

	/**
	 * Konstruktur
	 * 
	 * @param kategorien
	 *            die eingelesenen Kategorien
	 */
	public IWWWDNW(Kategorie[] kategorien) {
		this.kategorien = kategorien;
	}

	/**
	 * Startet das Spiel
	 */
	public void spielInitialisieren() {
		spielKategorien = new int[4];
		schiesseEvent(Status.ANZAHL_SPIELER_FESTLEGEN);
	}

	// oeffentliche Logik Methoden

	public void setAnzahlSpieler(int anzahl) {
		anzahlSpieler = anzahl;
		spielerNamen = new String[anzahl];
		spielfeld = new Spielfeld(anzahl);
		schiesseEvent(Status.NAMEN_SETZEN_1);
	}

	/**
	 * Steuert den Ablauf des Auswahl von 4 Kategorien aus den eingelesenen
	 * 
	 * @param katNummer
	 *            die existierende Nummer einer Kategorie
	 * @param katId
	 *            die von Spieler eingegene Id (Nummer) einer gewünschten
	 *            Kategorie
	 */
	public void waehleSpielKategorie(int katNummer, int katId) {
		spielKategorien[katNummer] = katId;
		if ((katNummer + 1) < ANZAHL_KATEGORIEN) {
			switch (katNummer) {
			case 0:
				schiesseEvent(Status.KATEGORIE_WAEHLEN_2);
				break;
			case 1:
				schiesseEvent(Status.KATEGORIE_WAEHLEN_3);
				break;
			case 2:
				schiesseEvent(Status.KATEGORIE_WAEHLEN_4);
				break;
			}
		} else {
			spielbeginnerErmitteln();
		}
	}

	/**
	 * Waehlt eine zufaellige Frage aus einer Kategorie aus
	 * 
	 * @param kategorieId
	 *            Nummer der Kategorie aus der gewaehlt werden soll
	 */
	public void waehleFrageKategorie(int kategorieId) {
		momentaneKategorieId = kategorieId;

		for (Kategorie k : kategorien) {
			if (k.getId() == spielKategorien[kategorieId]) {
				momentaneFrage = k.getFragen().get(
						RandInt.get(0, k.getFragen().size()));
				break;
			}
		}

		schiesseEvent(Status.ANTWORTEN);
	}

	/**
	 * Speichert die Namen der Mitspieler, je nach Anzahl derer
	 * 
	 * @param id
	 *            eindeutige Id eines Mitspielers
	 * @param name
	 *            Name des Mitspielers
	 */
	public void setName(int id, String name) {
		spielerNamen[id] = name.toUpperCase();

		if ((id + 1) < this.anzahlSpieler) {
			switch (id) {
			case 0:
				schiesseEvent(Status.NAMEN_SETZEN_2);
				break;
			case 1:
				schiesseEvent(Status.NAMEN_SETZEN_3);
				break;
			case 2:
				schiesseEvent(Status.NAMEN_SETZEN_4);
				break;
			}
		} else {
			schiesseEvent(Status.KATEGORIE_WAEHLEN_1);
		}
	}

	/**
	 * Ueberprueft die Richtigkeit der gegebenen Antwort und und veranlasst die
	 * Bewegung der Wissenstreiter und Wissenszeiger.
	 * 
	 * @param antwortId
	 *            die Id der gegebenen Antwort
	 */
	public void antwortGeben(int antwortId) {
		for (Antwort a : momentaneFrage.getAntworten()) {
			if (a.getId() == antwortId) {
				if (a.isRichtig()) {
					schiesseEvent(Status.ANTWORT_RICHTIG);
					spielfeld.wissensstreiterKollisionMitFrageBeantworten(
							momentanerSpieler, momentanerWissensstreiter,
							momentanKollidiertesFeld, true);
					if (spielfeld.wissenszeigerZugMoeglich(
							momentaneKategorieId,
							momentanKollidiertesFeld.getSpieler())) {
						spielfeld.wissenszeigerBewegen(momentaneKategorieId,
								momentanKollidiertesFeld.getSpieler(), 1);
					} else {
						schiesseEvent(Status.ANDEREN_WISSENZEIGER_WAEHLEN);
					}
				} else {
					schiesseEvent(Status.ANTWORT_FALSCH);
					spielfeld.wissensstreiterKollisionMitFrageBeantworten(
							momentanerSpieler, momentanerWissensstreiter,
							momentanKollidiertesFeld, false);
					spielfeld.wissenszeigerBewegen(momentaneKategorieId,
							momentanKollidiertesFeld.getSpieler(), -1);

					schiesseEvent(Status.GEGENANTWORTEN);
				}

				momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
				spielzugDurchfuehren();
			}
		}
	}

	/**
	 * Bearbeitet den Fall bei falscher Antwort des kollidierten Spielers so
	 * dass der aktive Spieler die Moeglichkeit hat die Frage zu beantworten
	 * 
	 * @param antwortId
	 *            die Id der gegebenen Antwort
	 */
	public void gegenantwortGeben(int antwortId) {
		for (Antwort a : momentaneFrage.getAntworten()) {
			if (a.getId() == antwortId) {
				if (a.isRichtig()) {
					schiesseEvent(Status.ANTWORT_RICHTIG);

					if (spielfeld.wissenszeigerZugMoeglich(
							momentaneKategorieId, momentanerSpieler)) {
						spielfeld.wissenszeigerBewegen(momentaneKategorieId,
								momentanerSpieler, 1);
					} else {
						schiesseEvent(Status.GEGENANTWORT_ANDEREN_WISSENZEIGER_WAEHLEN);
					}
				} else {
					schiesseEvent(Status.ANTWORT_FALSCH);

					spielfeld.wissenszeigerBewegen(momentaneKategorieId,
							momentanerSpieler, -1);
				}

				momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
				spielzugDurchfuehren();
			}
		}
	}

	/**
	 * Bewegt einen Wissenstreiter
	 * 
	 * @param wissensstreiterId
	 *            die Id des zu bewegenden Wissenstreiters
	 */
	public void wissensstreiterBewegen(int wissensstreiterId) {
		if (!wissensstreiterZugMoeglich(wissensstreiterId)) {
			return;
		}

		momentanKollidiertesFeld = spielfeld.wissensstreiterBewegen(
				wissensstreiterId, momentanerSpieler, momentanerWurf);

		if (momentanKollidiertesFeld != null) {
			momentanerWissensstreiter = wissensstreiterId;
			schiesseEvent(Status.KATEGORIE_FUER_FRAGE_WAEHLEN);
			momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
		} else {
			momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
			spielzugDurchfuehren();
		}
	}

	/**
	 * Bewegt einen Wissenszeiger
	 * 
	 * @param wissensanzeigerId
	 *            die Id des zu bewegenden Wissenszeigers
	 */
	public void wissensanzeigerBewegen(int wissensanzeigerId) {
		spielfeld.wissenszeigerBewegen(wissensanzeigerId,
				momentanKollidiertesFeld.getSpieler(), 1);
	}

	/**
	 * Bewegt einen Wissenszeiger bei einer Gegenantwort
	 * 
	 * @param wissensanzeigerId
	 *            die Id des zu bewegenden Wissenszeigers
	 */
	public void gegenantwortWissensanzeigerBewegen(int wissensanzeigerId) {
		spielfeld.wissenszeigerBewegen(wissensanzeigerId, momentanerSpieler, 1);
	}

	// oeffentliche Getter fur die Informations-Anzeige
	// z.B. Spielfeld anzeigen etc

	/**
	 * Ueberprueft ob eine Kategorie fuer das Spiele schon gewaehlt wurde
	 * 
	 * @return true wenn ja, sonst false
	 */
	public boolean kategorieSchonGewaehlt(int katId) {
		for (int i = 0; i < spielKategorien.length; i++) {
			if (spielKategorien[i] == katId) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Ueberprueft ob die Kategorie mit den Id ueberhaupt existiert
	 * 
	 * @param katId
	 *            die zu pruefenden Id der Kategorie
	 * @return true wenn sie existiert, sonst false
	 */
	public boolean existiertKategorie(int katId) {
		for (Kategorie k : kategorien) {
			if (k.getId() == katId) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Ueberprueft ob der Name eines Spielers schon existiert
	 * 
	 * @param name
	 *            Name des zu pruefenden Spielers
	 * @return true wenn ja, sonst false
	 */
	public boolean existiertName(String name) {
		for (String n : spielerNamen) {
			if (n != null && n.equals(name.toUpperCase())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Ist Zug mit Wissenstreiter moeglich
	 * 
	 * @param wissensstreiterId
	 *            die Id des Wissenstreiters
	 * @return true wenn ja, sonst false
	 */
	public boolean wissensstreiterZugMoeglich(int wissensstreiterId) {
		return spielfeld.wissensstreiterZugMoeglich(wissensstreiterId,
				momentanerSpieler, momentanerWurf);
	}

	/**
	 * Ist Zug mit Wissenszeiger moeglich
	 * 
	 * 
	 * @param wissenszeigerId
	 *            die Id des Wissenszeigers
	 * @return true wenn ja, sonst false
	 */
	public boolean wissensanzeigerZugMoeglich(int wissenszeigerId) {
		return spielfeld.wissenszeigerZugMoeglich(wissenszeigerId,
				momentanKollidiertesFeld.getSpieler());
	}

	/**
	 * Ist Zug mit Wissenszeiger bei Gegenantwort moeglich
	 * 
	 * 
	 * @param wissenszeigerId
	 *            die Id des Wissenszeigers
	 * @return true wenn ja, sonst false
	 */
	public boolean gegenantwortWissensanzeigerZugMoeglich(int wissenszeigerId) {
		return spielfeld.wissenszeigerZugMoeglich(wissenszeigerId,
				momentanerSpieler);
	}

	/**
	 * Ermittle Gewinner
	 * 
	 * @return Name des Gewinners, sonst nichts
	 */
	public String getGewinner() {
		int gewonnen = spielfeld.hatSpielerGewonnen();

		if (gewonnen == -1) {
			return "";
		}

		return spielerNamen[gewonnen];
	}

	/**
	 * Zeige eingelese Kategorien
	 */
	public void zeigeKategorien() {
		for (int i = 0; i < kategorien.length; i++) {
			System.out.println(kategorien[i].getText() + ": "
					+ kategorien[i].getId());
		}
		System.out.println();
	}

	/**
	 * Zeige die 4 fuer das Spiel ausgewaehlten Kategorien
	 */
	public void zeigeSpielKategorien() {
		for (int i = 0; i < spielKategorien.length; i++) {
			for (Kategorie k : kategorien) {
				if (k.getId() == spielKategorien[i]) {
					System.out.println(k.getText() + ": " + (i + 1));
				}
			}
		}
	}

	/**
	 * Zeige das Spielfeld an
	 */
	public void zeigeSpielfeld() {
		zeigeStartFelderInKonsole();
		zeigeSpielfeldInKonsole();
		zeigeWissenszeigerInKonsole();
	}

	/**
	 * Zeige eine Frage an
	 */
	public void zeigeFrage() {
		for (Kategorie k : kategorien) {
			if (k.getId() == spielKategorien[momentaneKategorieId]) {
				System.out.println();
				System.out.println("Kategorie: " + k.getText());
				System.out.println("Frage: " + momentaneFrage.getText());

				for (Antwort a : momentaneFrage.getAntworten()) {
					System.out.println(a.getId() + ": " + a.getText());
				}

				break;
			}
		}
	}

	/**
	 * Der aktuelles Spielstatus
	 * 
	 * @return aktuellen Status (Zustand) des Spiels
	 */
	public Status getSpielStatus() {
		return status;
	}

	/**
	 * Der aktuelle Spieler, der am Zug ist
	 * 
	 * @return Name des aktuellen Spielers
	 */
	public String getMomentanerSpieler() {
		return spielerNamen[momentanerSpieler];
	}

	/**
	 * Der Spieler der kollidiert wurde
	 * 
	 * @return Name des Spielers der kollidiert wurde
	 */
	public String getMomentanKollidierterSpieler() {
		return spielerNamen[momentanKollidiertesFeld.getSpieler()];
	}

	/**
	 * Gibt die gewuerfelte Augenzahl zurueck
	 * 
	 * @return aktuelle Anzahl der Wuerfel
	 */
	public int getMomentanerWurf() {
		return momentanerWurf;
	}

	// private Helfermethoden

	/**
	 * Schiesst ein Event und informiert die registrierten Objekte
	 */
	private void schiesseEvent(Status status) {
		this.status = status;

		setChanged();
		notifyObservers();
	}

	/**
	 * Ausgabe fuer die Startfelder in der Konsole
	 */
	private void zeigeStartFelderInKonsole() {
		System.out.println();
		System.out.println("Heimatfelder:");

		for (int i = 0; i < anzahlSpieler; i++) {
			for (int j = 0; j < 3; j++) {
				System.out.print("|");

				if (spielfeld.getWissensstreiterHeimatFeld()[i][j]
						.getWissensstreiterId() == 0) {
					System.out.print("   ");
				} else {
					System.out.print(spielerNamen[i]
							+ spielfeld.getWissensstreiterHeimatFeld()[i][j]
									.getWissensstreiterId());
				}
			}

			System.out.print("|  ");
		}

		System.out.println();
		System.out.println();
	}

	/**
	 * Ausgabe fuer das Spielfeld in der Konsole
	 */
	private void zeigeSpielfeldInKonsole() {
		System.out.println("Spielfeld:");

		for (int i = 0; i < 4; i++) {
			System.out.print("|");

			for (int j = 0; j < 12; j++) {
				if (spielfeld.getWissensstreiterFelder()[i * 12 + j]
						.getWissenstreiterId() != 0) {
					System.out.print(spielerNamen[spielfeld
							.getWissensstreiterFelder()[i * 12 + j]
							.getSpieler()]
							+ spielfeld.getWissensstreiterFelder()[i * 12 + j]
									.getWissenstreiterId());
				} else {
					System.out.print("   ");
				}

				System.out.print("|");
			}

			System.out.println();
		}

		System.out.println();
	}

	/**
	 * Ausgabe fuer die Wissenszeiger in der Konsole
	 */
	private void zeigeWissenszeigerInKonsole() {
		System.out.println("Wissenzeiger:");

		for (int i = 0; i < anzahlSpieler; i++) {
			System.out.print(spielerNamen[i] + "   ");

			for (int j = 0; j < 4; j++) {
				System.out.print("K" + (j + 1) + " : "
						+ spielfeld.getWissenszeigerStatus()[i][j] + "   ");
			}

			System.out.println();
		}
	}

	/**
	 * Ermittelt den Beginner des Spiels gemaess den Regeln
	 */
	private void spielbeginnerErmitteln() {
		schiesseEvent(Status.BEGINNER_ERMITTELN);

		boolean beginnerErmittelt = false;
		int[] beginnerErmittelnWuerfe = new int[anzahlSpieler];
		momentanerSpieler = 0;

		while (!beginnerErmittelt) {
			boolean habeAlleGewurfelt = true;

			for (int w : beginnerErmittelnWuerfe) {
				if (w == 0) {
					habeAlleGewurfelt = false;
				}
			}

			if (habeAlleGewurfelt) {
				int hoechsteZahl = 0;

				for (int w : beginnerErmittelnWuerfe) {
					if (w > hoechsteZahl) {
						hoechsteZahl = w;
					}
				}

				int istDoppelt = 0;

				for (int w : beginnerErmittelnWuerfe) {
					if (w == hoechsteZahl) {
						istDoppelt++;
					}
				}

				if (istDoppelt > 1) {
					for (int i = 0; i < beginnerErmittelnWuerfe.length; i++) {
						if (beginnerErmittelnWuerfe[i] == hoechsteZahl) {
							momentanerWurf = RandInt.wuerfeln();
							beginnerErmittelnWuerfe[i] = momentanerWurf;
							momentanerSpieler = i;
							schiesseEvent(Status.SPIELERWURF);
						} else {
							beginnerErmittelnWuerfe[i] = -1;
						}
						momentanerSpieler = 0;
					}
				} else {
					for (int i = 0; i < beginnerErmittelnWuerfe.length; i++) {
						if (beginnerErmittelnWuerfe[i] == hoechsteZahl) {
							momentanerSpieler = i;
							beginnerErmittelt = true;
							break;
						}
					}
				}
			} else {
				momentanerWurf = RandInt.wuerfeln();
				beginnerErmittelnWuerfe[momentanerSpieler] = momentanerWurf;
				schiesseEvent(Status.SPIELERWURF);
				momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
			}
		}

		schiesseEvent(Status.SPIEL_STARTEN);
		spielzugDurchfuehren();
	}

	/**
	 * Fuerht einen Spielzug durch
	 */
	private void spielzugDurchfuehren() {

		if (spielfeld.hatSpielerGewonnen() >= 0) {
			schiesseEvent(Status.SPIEL_VORBEI);
			return;
		}

		momentanerWurf = 0;
		if (spielfeld.sindAlleHeimatfelderBesetzt(momentanerSpieler)) {
			int zaehler = 0;

			while (zaehler < MAX_WUERFE && momentanerWurf != 6) {
				momentanerWurf = RandInt.wuerfeln();
				schiesseEvent(Status.SPIELERWURF);
				zaehler++;
			}

			if (zaehler <= 3 && momentanerWurf == 6) {
				schiesseEvent(Status.WISSENSSTREITER_BEWEGEN);
			} else {
				momentanerSpieler = (momentanerSpieler + 1) % anzahlSpieler;
				spielzugDurchfuehren();
			}
		} else {
			momentanerWurf = RandInt.wuerfeln();
			schiesseEvent(Status.SPIELERWURF);
			schiesseEvent(Status.WISSENSSTREITER_BEWEGEN);
		}
	}
}
